/**
 *  @file SymmetricCipher.h
 */
#ifndef _SYMMETRICCIPHER_H_
#define _SYMMETRICCIPHER_H_

#include "../../common/BaseObject.h"
#include "../../text/CFString.h"
#include "../../collections/ArrayT.h"

namespace cppflib
{

namespace security
{

namespace crypto
{

/**
 *  Class for symmetric ciphers. This class support CBC block cipher
 */
class _DLLAPI SymmetricCipher : public BaseObject
{
public:
    /**
     *  Cipher alogorithm
     *  Important: Not all are present. They are just listed here.
     *             (AES, DES, 3DES3 are sure to be supported)
     */
    typedef enum _cipheralgo 
    {
       CA_NULL, // null cipher which does nothing

       CA_AES128,     // AES: 128 bit key, 128 bit block
       CA_AES192,     // AES: 192 bit key, 128 bit block
       CA_AES256,     // AES: 256 bit key, 128 bit block
       CA_DES,        // DES: 64 bit key, 64 bit block
       CA_3DES3,      // 3DES (three 64 bit keys -- 'GetKeyLength' will return 24 bits)
       CA_IDEA,       // IDEA: 128 bit key, 64 bit block 
       CA_RC2,        // rc2: default to 128 bit key
       CA_CAMELLIA128,// camellia: 128 bit key, 128 bit block 
       CA_CAMELLIA256,// camellia: 256 bit key, 128 bit block
       CA_SEED,       // seed: 128 bit key, 128 bit block
       
    } cipheralgo_t;

    typedef enum _blockmode
    {
       BM_NONE,
       BM_CBC         // Cipher Block Chaining
    } blockmode_t;

private:
    SymmetricCipher(void);
    void ReleaseBeforeThrowing();
    void SetKeyLength(u32_t lenInByte);  // length in byte

private:
    cipheralgo_t cipherAlgo;
    blockmode_t blockMode;
    bool isEncrypt;
    ptrany_t cipherCtxt;

public:
    SymmetricCipher(cipheralgo_t cipherAlgo, blockmode_t blockMode, bool isEncrypt);
    virtual ~SymmetricCipher(void);

    bool Reinitialize();

    CFString GetCipherName();
    cipheralgo_t GetCipherAlgorithm();
    blockmode_t GetBlockMode();
    bool IsForEncryption();

    void SetKey(collections::ArrayByte & key);
    void SetKeyAndIV(collections::ArrayByte & key, collections::ArrayByte & iv);
    void EnablePadding(bool enabled);    // enabled by default (PKCS7)

    u32_t GetKeyLength();   // length in byte 
    u32_t GetBlockSize();   // length in byte
    u32_t GetIVLength();    // length in byte
    
    bool EncryptUpdate(collections::ArrayByte &out, u32_t outOffset,
                       collections::ArrayByte &inData, u32_t inOffset, u32_t &offsetUpdate);
    bool EncryptFinal(collections::ArrayByte &out, u32_t outOffset,
                      collections::ArrayByte &inData, u32_t inOffset, u32_t inLen,
                      u32_t &offsetUpdate);

    bool DecryptUpdate(collections::ArrayByte &out, u32_t outOffset, 
                       collections::ArrayByte &inData, u32_t inOffset, u32_t &offsetUpdate);
    bool DecryptFinal(collections::ArrayByte &out, u32_t outOffset, u32_t &offsetUpdate);
};

} // end of namespace crypto

} // end of namespace security

} // end of namespace cppflib

#endif
